커맨드 패턴
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
커맨드 패턴은 객체의 요청을 캡슐화하여 호출자와 수신자를 분리하는 디자인 패턴이다. 이 패턴은 요청 처리와 구현 처리를 분리하고, Command 객체를 독립적으로 만들어 큐잉, 비동기 처리, 스케줄링, 히스토리 저장, 로깅, 실행 취소 등의 기능을 가능하게 한다. GUI 버튼, 매크로 기록, 다단계 실행 취소, 네트워킹, 병렬 처리, 진행률 표시줄, 스레드 풀, 데이터베이스 트랜잭션, 마법사 등 다양한 분야에서 활용되며, C++, Java, Python 등 여러 프로그래밍 언어로 구현될 수 있다. 커맨드 패턴은 헨리 리버만이 1985년에 처음 언급했으며, 버트란드 메이어의 저서를 통해 실행 취소-다시 실행 메커니즘으로 소개되었다.
더 읽어볼만한 페이지
- 소프트웨어 디자인 패턴 - 모델-뷰-컨트롤러
모델-뷰-컨트롤러(MVC)는 소프트웨어 디자인 패턴으로, 응용 프로그램을 모델, 뷰, 컨트롤러 세 가지 요소로 분리하여 개발하며, 사용자 인터페이스 개발에서 데이터, 표현 방식, 사용자 입력 처리를 분리해 유지보수성과 확장성을 높이는 데 기여한다. - 소프트웨어 디자인 패턴 - 스케줄링 (컴퓨팅)
스케줄링은 운영 체제가 시스템의 목적과 환경에 맞춰 작업을 관리하는 기법으로, 장기, 중기, 단기 스케줄러를 통해 프로세스를 선택하며, CPU 사용률, 처리량 등을 기준으로 평가하고, FCFS, SJF, RR 등의 알고리즘을 사용한다.
| 커맨드 패턴 | |
|---|---|
| 개요 | |
![]() | |
| 종류 | 행동 디자인 패턴 |
| 목적 | 요청을 객체로 캡슐화하여, 서로 다른 요청을 큐에 넣거나 로깅할 수 있게 하고, 연산 매개변수화도 가능하게 함 작업 실행에 필요한 모든 정보를 캡슐화 |
| 동기 | 응용 프로그램의 명령 기반 GUI 트랜잭션 기반 지속성 |
| 구조 | |
| 참여자 | Client (클라이언트) Invoker (호출자) Command (명령) ConcreteCommand (구체적인 명령) Receiver (수신자) |
| 협업 | 클라이언트는 ConcreteCommand 객체를 생성하고 Receiver를 설정한다. Invoker는 Command 객체를 통해 작업을 요청한다. ConcreteCommand는 Receiver의 작업을 호출하여 요청을 수행한다. |
| 결과 | |
| 장점 | 명령을 클래스처럼 조작하고 확장할 수 있다. 명령을 큐에 넣거나 로깅할 수 있다. 명령 패턴을 사용하여 시스템을 구축할 수 있다. |
| 단점 | 많은 ConcreteCommand 클래스를 생성해야 할 수 있다. |
| 구현 | |
| 고려 사항 | 명령 객체의 수명 주기 관리가 중요하다. |
| 예시 | 텍스트 편집기에서 "잘라내기", "붙여넣기" 등의 기능 |
| 관련 패턴 | |
| 관련 패턴 | 책임 연쇄 패턴, 메멘토 패턴 |
2. 구조
커맨드 패턴[1]은 유연하고 재사용 가능한 객체 지향 소프트웨어를 설계하기 위해 반복적인 설계 문제를 해결하는 방법을 설명하는 23가지 잘 알려진 ''GoF 디자인 패턴'' 중 하나이다. 이는 구현, 변경, 테스트 및 재사용이 더 쉬운 객체를 만드는 데 도움을 준다.
커맨드 디자인 패턴은 다음과 같은 문제를 해결하기 위해 사용될 수 있다.[2]
- 요청을 호출하는 객체(Invoker)를 특정 요청에 직접 연결하는 것을 피한다. 즉, 하드 코딩된 요청을 사용하지 않는다.
- 요청을 사용하여 객체(Invoker)를 구성할 수 있어야 한다.
요청을 클래스 내부에 직접 구현(하드 코딩)하면 유연성이 떨어진다. 컴파일 시점에 클래스가 특정 요청에 묶이게 되어, 런타임에 다른 요청을 지정할 수 없기 때문이다.
커맨드 디자인 패턴은 이 문제에 대해 다음과 같은 해결책을 제시한다.
- 요청 자체를 캡슐화하는 별도의 '''Command''' 객체를 정의한다.
- 클래스는 특정 요청을 직접 구현하는 대신, 해당 요청을 '''Command''' 객체에게 위임한다.
이를 통해 클래스는 자신이 사용할 '''Command''' 객체를 외부에서 설정받아 구성될 수 있다. 클래스는 더 이상 특정 요청에 종속되지 않으며, 요청이 구체적으로 어떻게 수행되는지에 대해 알 필요 없이 독립적으로 작동할 수 있다.
커맨드 패턴에서는 어떤 요청을 실행할 때, 단순히 해당 처리를 직접 호출하는 대신 다음과 같은 단계를 따른다.[8]
# 처리를 메서드로 포함하는 '''Command''' 클래스를 정의한다.
# '''Command''' 객체를 생성한다.
# 생성된 '''Command''' 객체의 `execute()` 메서드를 호출하여 요청을 실행한다.
즉, 요청을 "명령서"의 정의, 생성, 그리고 실행이라는 단계로 나누어 처리하는 방식이다.
'''Command''' 객체가 포함할 처리 내용을 구현하는 방식은 개발자에게 달려있다. 모든 처리를 '''Command''' 객체 내부에 직접 기술하는 방식도 있고[9], '''Command''' 객체 생성 시 실제 작업을 수행할 '''Receiver''' 객체를 받아두었다가, `execute()` 메서드가 호출되면 '''Receiver''' 객체의 특정 메서드(예: `action()`)를 호출하는 방식으로 구현할 수도 있다. 이 경우 '''Command''' 객체는 요청 실행을 '''Receiver'''에게 위임하고 연결 역할에 집중하게 된다[10]. '''Receiver'''는 커맨드 패턴의 필수 구성 요소는 아니다.
'''Command'''는 경우에 따라 '''Action''', '''Transaction'''[11] 또는 '''Task'''[12]라고도 불린다.
2. 1. UML 클래스 다이어그램
위 UML 클래스 다이어그램에서 호출자(Invoker) 클래스는 요청을 직접 구현하지 않는다. 대신 호출자는 요청을 수행하기 위해 Command 인터페이스를 참조하여 `execute()` 메소드를 호출한다. 이렇게 하면 호출자는 요청이 구체적으로 어떻게 수행되는지에 대해 알 필요 없이 독립적으로 동작할 수 있다.
Command1과 같은 구체적인 Command 클래스는 Command 인터페이스를 구현한다. 이 클래스는 특정 작업을 수행하는 수신자(Receiver) 객체의 메소드(예: `receiver1.action1()`)를 호출하여 실제 요청을 처리한다.
3. 특징
커맨드 패턴은 어떤 요청을 실행할 때, 단순히 처리를 실행하는 것이 아니라 다음 단계를 따른다.
# 처리를 메서드로 내포하는 Command 클래스의 정의
# Command 객체의 생성
# `Command.execute()` 메서드의 호출에 의한 요청 실행[8]
즉, 요청을 "절차"의 정의·생성과 그 "실행"으로 단계별로 나누는 패턴을 취한다.
Command가 내포하는 처리 방식은 개발자에게 달려있다. 모든 처리를 Command 내에 기술하는 패턴[9], 혹은 `command` 인스턴스 생성 시 실행자(Receiver 클래스의 인스턴스 `receiver`, 수신자 또는 관측자라고도 함)를 받아 `command.execute()` 시 `receiver.action()` 호출만을 수행하는 패턴도 있다. 즉, 실행을 `Receiver`에게 위임하고 `Command`는 연결 역할에 전념하는 방식이다[10]. 덧붙여, Receiver는 커맨드 패턴의 필수 요건은 아니다.
`Command`는 Action, Transaction[11] 또는 Task[12]라고도 불린다.
이 패턴이 가진 장점 중 하나는 요청 처리와 구현 처리의 분리(느슨한 결합)이다.[13]
예를 들어, 버튼 클릭으로 요청을 실행할 수 있는 UI 프레임워크를 개발한다고 가정해 보자. 버튼의 클릭 기능과 요청의 실행은 프레임워크의 책임이지만, 요청 실행으로 구체적으로 무엇이 일어나는지는 애플리케이션의 책임이다. 즉, UI 프레임워크 측에서는 클릭에 따라 요청을 발행하지만, 요청에 대해 어떤 처리가 이루어지는지, 더 나아가 요청의 수신자가 누구인지에 대해서는 관여하지 않는다.[14] 여기서 버튼 생성 시 `Command`를 받도록 한다. UI 프레임워크 측에서는 `Command`가 어떤 처리를 내포하고 있는지 (캡슐화되어 있어서) 알 수 없지만, `execute()` 메서드를 실행하면 요청이 실행된다는 것을 알고 있다. 이 인터페이스를 통한 계약에 따라, 클릭 시 `command.execute()`만 하면 클릭에 응답하는 요청을 실행할 수 있다. 이처럼 커맨드 패턴은 Command의 실행과 구현을 느슨하게 결합할 수 있다. 바꿔 말하면, 커맨드 패턴은 `Command` 객체의 DI에 의한 처리와 실행의 분리(관심사 분리)이다. 이는 절차적 프로그래밍에서의 콜백(callback)에 해당한다.[15]
이 패턴이 가진 또 다른 장점은 Command가 독립적인 객체라는 점이다. 실행되지 않은 `Command` 객체를 배열에 넣어 큐잉하면 비동기 처리 및 스케줄링이 가능해진다. 또한, 실행 완료된 `Command`를 큐잉하면 히스토리 저장 및 로깅, 실행 취소(Undo) 등의 기능을 구현할 수 있다.
4. 활용
커맨드 패턴은 다양한 상황에서 유용하게 활용될 수 있다. 주요 활용 사례는 다음과 같다.
; GUI 버튼 및 메뉴 항목
: 스윙, 보르랜드 델파이, WPF 등 그래픽 사용자 인터페이스(GUI) 프레임워크에서 버튼 클릭이나 메뉴 선택과 같은 사용자 입력을 처리하는 데 사용된다. 예를 들어, 자바 스윙의 `Action` 인터페이스[1]나 WPF의 `ICommand` 인터페이스[17]는 커맨드 객체로 사용된다. 이 객체는 단순히 명령을 실행하는 기능 외에도 아이콘, 키보드 단축키, 툴팁 텍스트 등의 정보를 포함할 수 있어, 툴바 버튼이나 메뉴 항목을 해당 객체만으로 완전히 구성할 수 있다.[1][16]
; 매크로 기록
: 사용자의 모든 작업이 커맨드 객체로 표현된다면, 프로그램은 실행된 커맨드 객체들을 순서대로 목록에 저장하여 사용자 작업을 기록할 수 있다. 이후 이 목록의 커맨드 객체들을 같은 순서로 다시 실행하면 기록된 작업을 그대로 재현하는 매크로 기능을 구현할 수 있다. 각 커맨드 객체에 스크립트 변환 기능을 추가하면 사용자 작업을 스크립트로 쉽게 저장할 수도 있다.[1]
; 다단계 실행 취소 및 다시 실행
: 프로그램의 모든 사용자 작업이 커맨드 객체로 구현되어 있다면, 가장 최근에 실행된 명령들의 스택을 유지하여 실행 취소(Undo) 기능을 구현할 수 있다. 사용자가 실행 취소를 원하면, 프로그램은 스택에서 가장 최근의 커맨드 객체를 꺼내 해당 객체의 `undo()` 메서드를 실행한다. 마찬가지로, 취소된 명령들을 별도의 스택에 저장하여 다시 실행(Redo) 기능도 구현할 수 있다.[1]
; 트랜잭션 처리
: 여러 단계를 거치는 작업을 하나의 논리적인 단위(트랜잭션)로 묶어 처리할 때 유용하다. 예를 들어 데이터베이스 업데이트나 소프트웨어 설치 과정에서 여러 작업 중 하나라도 실패하면, 이전 작업들을 모두 원래 상태로 되돌리는 롤백(Rollback) 기능이 필요하다. 커맨드 패턴을 사용하면 각 작업을 커맨드 객체로 만들고, 작업 목록을 관리하여 트랜잭션 처리 및 롤백을 구현할 수 있다. 2단계 커밋 구현에도 사용될 수 있다.[1]
; 비동기 작업 처리 (스레드 풀, 병렬 처리)
: 커맨드 객체를 작업 큐에 넣고, 별도의 스레드 풀에서 이 큐의 작업들을 비동기적으로 처리할 수 있다. 스레드 풀은 특정 작업 내용을 알 필요 없이, 공통 인터페이스(예: 자바의 `Runnable`[18])를 구현한 커맨드 객체를 실행하기만 하면 된다.[1] 또한, 커맨드를 여러 스레드나 여러 컴퓨터에서 병렬로 실행하여 처리 속도를 높일 수도 있다. 이는 종종 마스터/워커 패턴이라고 불린다.[1]
; 네트워킹
: 커맨드 객체를 네트워크를 통해 다른 컴퓨터로 전송하여 원격에서 실행할 수 있다. 예를 들어, 온라인 게임에서 플레이어의 이동이나 공격 같은 행동을 커맨드 객체로 만들어 서버나 다른 클라이언트로 전송하여 처리할 수 있다.[1]
; 진행률 표시
: 여러 커맨드를 순서대로 실행해야 하는 작업에서, 각 커맨드 객체가 예상 소요 시간을 알려주는 기능을 갖는다면, 프로그램은 전체 작업의 예상 완료 시간과 현재 진행률을 계산하여 사용자에게 진행률 표시줄 형태로 보여줄 수 있다.[1]
; 마법사(Wizard)
: 여러 페이지에 걸쳐 사용자로부터 설정을 입력받고 마지막 단계에서 한 번에 작업을 실행하는 마법사 인터페이스 구현에 유용하다. 마법사가 시작될 때 커맨드 객체를 생성하고, 각 페이지에서 사용자가 입력한 설정값을 이 객체에 저장한다. 마지막 페이지에서 '완료' 버튼을 누르면 저장된 정보가 담긴 커맨드 객체의 `execute()` 메서드를 호출하여 작업을 실행한다. 이를 통해 사용자 인터페이스 코드와 실제 작업 처리 로직을 분리할 수 있다.[1]
; 모바일 코드
: 자바의 `URLClassLoader`처럼 코드를 원격지에서 불러와 실행할 수 있는 환경에서는, 커맨드 객체를 통해 새로운 기능을 원격지에 전달하고 실행시킬 수 있다 (예: EJB 커맨드).[1]
5. 구현 예제
커맨드 패턴은 다양한 프로그래밍 언어를 사용하여 구현될 수 있다. 아래 하위 섹션에서는 C++, Java, Python을 이용한 구체적인 구현 예제를 살펴본다.
5. 1. C++
다음은 C++를 이용한 커맨드 패턴 구현 예제이다.첫 번째 예제는 C++98 이전 스타일의 구현이다. 추상 클래스인 `Command`를 정의하고, 이를 상속받는 구체적인 커맨드 클래스 `Ingredient`와 `Step`를 만든다. `CmdStack` 클래스는 커맨드 객체들을 벡터에 저장하고 관리하며, 순차적으로 실행하거나 마지막 명령을 취소하는 기능을 제공한다.
#include
#include
#include
using namespace std;
// Command 인터페이스
class Command{
public:
virtual void execute(void) =0; // 실행 메서드
virtual ~Command(void){}; // 가상 소멸자
};
// 구체적인 Command: 재료 추가
class Ingredient : public Command {
public:
Ingredient(string amount, string ingredient){
_ingredient = ingredient;
_amount = amount;
}
// 재료 추가 로직 실행
void execute(void){
cout << " *Add " << _amount << " of " << _ingredient << endl;
}
private:
string _ingredient;
string _amount;
};
// 구체적인 Command: 조리 단계
class Step : public Command {
public:
Step(string action, string time){
_action= action;
_time= time;
}
// 조리 단계 로직 실행
void execute(void){
cout << " *" << _action << " for " << _time << endl;
}
private:
string _time;
string _action;
};
// Invoker: 커맨드 관리 및 실행
class CmdStack{
public:
// 커맨드 추가
void add(Command *c) {
commands.push_back(c);
}
// 저장된 모든 커맨드 순차 실행 (레시피 생성)
void createRecipe(void){
for(vector
commands[x]->execute();
}
}
// 마지막 커맨드 취소 (undo)
void undo(void){
if(commands.size() >= 1) {
commands.pop_back();
}
else {
cout << "Can't undo" << endl;
}
}
private:
vector
};
int main(void) {
CmdStack list; // Invoker 객체 생성
// 재료 커맨드 객체 생성
Ingredient first("2 tablespoons", "vegetable oil");
Ingredient second("3 cups", "rice");
Ingredient third("1 bottle","Ketchup");
Ingredient fourth("4 ounces", "peas");
Ingredient fifth("1 teaspoon", "soy sauce");
// 조리 단계 커맨드 객체 생성
Step step("Stir-fry","3-4 minutes");
// 레시피 생성 과정 (커맨드 추가 및 실행)
cout << "Recipe for simple Fried Rice" << endl;
list.add(&first);
list.add(&second);
list.add(&step);
list.add(&third);
list.undo(); // 마지막 케첩 추가 명령 취소
list.add(&fourth);
list.add(&fifth);
list.createRecipe(); // 최종 레시피 실행
cout << "Enjoy!" << endl;
return 0;
}
다음은 C++14 표준을 사용한 구현이며, 앞서 제시된 C++98 이전의 구현을 기반으로 한다. 이 코드는 템플릿과 스마트 포인터를 활용하여 더 유연하고 안전한 코드를 작성한다.
#include
#include
// Command 인터페이스
class Command {
public:
// 연산을 실행하기 위한 인터페이스 선언
virtual void execute() = 0;
virtual ~Command() = default; // 기본 가상 소멸자
protected:
Command() = default; // 기본 생성자 (protected)
};
// ConcreteCommand: 템플릿을 사용하여 일반화된 커맨드
template
class SimpleCommand : public Command {
public:
// Receiver의 멤버 함수 포인터 타입 정의
typedef void (Receiver::* Action)();
// Receiver 객체와 실행할 액션(멤버 함수 포인터)을 받아 바인딩
SimpleCommand(std::shared_ptr
receiver(receiver_.get()), action(action_) { }
// 복사 생성자 및 할당 연산자 삭제 (Rule of Three/Five)
SimpleCommand(const SimpleCommand&) = delete;
const SimpleCommand& operator=(const SimpleCommand&) = delete;
// Receiver 객체의 지정된 액션(멤버 함수)을 호출하여 execute 구현
virtual void execute() override { // override 명시
(receiver->*action)();
}
private:
Receiver* receiver; // Receiver 객체 포인터 (스마트 포인터가 소유권 관리)
Action action; // 실행할 멤버 함수 포인터
};
// Receiver: 실제 작업을 수행하는 클래스
class MyClass {
public:
// 요청을 처리하는 방법(action)을 아는 클래스
void action() {
std::cout << "MyClass::action\n";
}
};
int main() {
// 스마트 포인터(shared_ptr)를 사용하여 Receiver 객체 생성 및 관리 (메모리 누수 방지)
std::shared_ptr
// 스마트 포인터(unique_ptr)를 사용하여 Command 객체 생성 및 관리
// SimpleCommand 템플릿에 Receiver 타입(MyClass)과 액션(MyClass::action)을 전달
std::unique_ptr
// Command 실행
command->execute();
return 0; // main 함수 반환 타입 명시
}
위 C++14 코드의 프로그램 출력은 다음과 같다.
MyClass::action
5. 2. Java
다음은 Java 언어를 사용하여 커맨드 패턴을 구현한 예시이다.```java
/* Invoker 클래스: 명령 실행을 요청하는 객체 */
public class Switch {
private Command flipUpCommand;
private Command flipDownCommand;
public Switch(Command flipUpCmd, Command flipDownCmd) {
this.flipUpCommand = flipUpCmd;
this.flipDownCommand = flipDownCmd;
}
public void flipUp() {
flipUpCommand.execute();
}
public void flipDown() {
flipDownCommand.execute();
}
}
/* Receiver 클래스: 실제 작업을 수행하는 객체 */
public class Light {
public Light() { }
public void turnOn() {
System.out.println("The light is on"); // 전등이 켜짐
}
public void turnOff() {
System.out.println("The light is off"); // 전등이 꺼짐
}
}
/* Command 인터페이스: 실행될 작업을 정의하는 인터페이스 */
public interface Command {
void execute();
}
/* ConcreteCommand 클래스: 전등을 켜는 명령 */
public class TurnOnLightCommand implements Command {
private Light theLight;
public TurnOnLightCommand(Light light) {
this.theLight = light;
}
@Override
public void execute() {
theLight.turnOn();
}
}
/* ConcreteCommand 클래스: 전등을 끄는 명령 */
public class TurnOffLightCommand implements Command {
private Light theLight;
public TurnOffLightCommand(Light light) {
this.theLight = light;
}
@Override
public void execute() {
theLight.turnOff();
}
}
/* Client 클래스: 커맨드 패턴을 테스트하는 클래스 */
public class TestCommand {
public static void main(String[] args) {
Light light = new Light(); // Receiver 객체 생성
// ConcreteCommand 객체 생성 및 Receiver 연결
Command switchUp = new TurnOnLightCommand(light);
Command switchDown = new TurnOffLightCommand(light);
// Invoker 객체 생성 및 Command 객체 설정
Switch s = new Switch(switchUp, switchDown);
// 명령 실행 요청
s.flipUp();
s.flipDown();
}
}
5. 3. Python
Python을 이용한 커맨드 패턴 구현 예시는 다음과 같다. 이 예제에서는 전등을 켜고 끄는 간단한 동작을 커맨드 패턴으로 구현한다.- `Light` 클래스: 실제 동작을 수행하는 수신자(Receiver) 역할을 한다. `turn_on`과 `turn_off` 메서드를 통해 전등을 켜고 끄는 구체적인 작업을 정의한다.
- `Switch` 클래스: 호출자(Invoker) 역할을 한다. 실행될 명령(이 경우, `Light` 객체의 메서드)을 저장하고 있다가 요청이 오면 해당 명령을 실행한다. `__init__` 메서드에서 켜는 명령(`flip_up_cmd`)과 끄는 명령(`flip_down_cmd`)을 인자로 받아 각각 `flip_up`, `flip_down` 속성에 저장한다.
- `LightSwitch` 클래스: 클라이언트(Client) 역할을 한다. `Light` 객체(수신자)와 `Switch` 객체(호출자)를 생성하고, 수신자의 메서드를 호출자의 명령으로 설정하여 연결한다. `switch` 메서드는 사용자로부터 "ON" 또는 "OFF" 문자열을 입력받아 해당하는 `Switch`의 메서드(`flip_up` 또는 `flip_down`)를 호출한다.
아래 코드는 전등(Light)을 켜고 끄는 명령을 스위치(Switch)를 통해 실행하는 과정을 보여준다.
class Switch(object):
"""호출자(Invoker) 클래스"""
def __init__(self, flip_up_cmd, flip_down_cmd):
self.flip_up = flip_up_cmd
self.flip_down = flip_down_cmd
class Light(object):
"""수신자(Receiver) 클래스"""
def turn_on(self):
print("전등이 켜졌습니다.")
def turn_off(self):
print("전등이 꺼졌습니다.")
class LightSwitch(object):
"""클라이언트(Client) 클래스"""
def __init__(self):
lamp = Light()
# Light 객체의 메서드를 직접 커맨드로 전달
self._switch = Switch(lamp.turn_on, lamp.turn_off)
def switch(self, cmd):
cmd = cmd.strip().upper()
if cmd == "ON":
self._switch.flip_up()
elif cmd == "OFF":
self._switch.flip_down()
else:
print("'ON' 또는 'OFF' 인자가 필요합니다.")
# 스크립트로 직접 실행될 때만 아래 코드를 실행
if __name__ == "__main__":
light_switch = LightSwitch()
print("Switch ON 테스트:")
light_switch.switch("ON")
print("Switch OFF 테스트:")
light_switch.switch("OFF")
print("잘못된 명령어 테스트:")
light_switch.switch("")
실행 결과는 다음과 같다.
Switch ON 테스트:
전등이 켜졌습니다.
Switch OFF 테스트:
전등이 꺼졌습니다.
잘못된 명령어 테스트:
'ON' 또는 'OFF' 인자가 필요합니다.
이처럼 파이썬에서는 함수나 메서드를 일급 객체로 취급할 수 있어, 별도의 커맨드 인터페이스나 클래스 없이도 수신자의 메서드를 직접 호출자에게 전달하여 커맨드 패턴을 구현할 수 있다.
6. 역사
1985년 헨리 리버만이 발표한 논문에서 대화형 시스템을 구현하기 위해 Command 클래스를 사용한 내용이 처음 언급된 것으로 보인다.[4] Command 클래스를 사용하여 'execute'(실행) 및 'undo'(취소) 메서드와 기록 목록을 갖춘 (다단계) 실행 취소-다시 실행 메커니즘에 대한 최초의 설명은 버트란드 메이어의 저서 객체 지향 소프트웨어 구성 초판(1988년) 12.2절에 처음 등장한다.[5]
참조
[1]
서적
Design Patterns: Elements of Reusable Object-Oriented Software
https://archive.org/[...]
Addison Wesley
[2]
웹사이트
The Command design pattern - Problem, Solution, and Applicability
http://w3sdesign.com[...]
2017-08-12
[3]
웹사이트
The Command design pattern - Structure and Collaboration
http://w3sdesign.com[...]
2017-08-12
[4]
간행물
There's more to menu systems than meets the screen
1985
[5]
서적
Object-Oriented Software Construction
Prentice-Hall
1988
[6]
문서
[7]
문서
[8]
문서
[9]
문서
[10]
문서
[11]
문서
[12]
문서
[13]
문서
[14]
문서
[15]
문서
[16]
웹사이트
Vcl.ActnList.TAction - RAD Studio API Documentation
https://docwiki.emba[...]
[17]
웹사이트
コマンド実行の概要 - WPF .NET Framework | Microsoft Learn
https://learn.micros[...]
[18]
웹사이트
ThreadPoolExecutor (Java 2 Platform SE 5.0)
http://java.sun.com/[...]
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com
